package com.mc.moring.base



import android.util.Base64
import android.util.Log
import com.google.gson.Gson
import com.xx.text.tool.encipher.Base64Utils.decode
import com.xx.text.tool.encipher.Base64Utils.encode
import java.io.ByteArrayOutputStream
import java.io.IOException
import java.security.*
import java.security.spec.InvalidKeySpecException
import java.security.spec.X509EncodedKeySpec
import javax.crypto.Cipher

/**
 * uncletan
 * on 2023/6/9
 * email tanhongchang@linghit.com
 *
 **/
object RSAUtil {
    private const val sTransform = "RSA/ECB/PKCS1Padding"
    private val TAG = "RSAUtil"

//    val publicKeyString = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlkgK7U33tXLHKJWn2PbhZWBhVWlwyVwiHsxYlEeLptbotHjnGZOdmpXpeeLOVQqnavxYEB7lJMA4TQtF9E1fZNEy9Wz7hDu1Z3rfwdpGDKPPh36jj/MWboD5tcZJ4yJbMdZ9Xc4b7CH6yZZEbB5dUlxQYqk4kbjyoklXxWuWpybtOEmD0RUaBn9Z1qyIjRCoCr2LF8tOO5N8ZJD62HpL5PGk4KMTUIR/FugYmiiCfZDW6g+T7VS1UdlxozUGhv4deAUqsoXqd5PUJbRleu2z3B8kDWmjx24YKVVv/RqhY3fr1WiJfbe17qhX8Z23BpHKfJ6JwhJIFs2uHGfUA0EaFQIDAQAB"
    val publicKeyString = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA8QObPlUJODI1G2HwvsRNadyZEKWnmDj43qqBQI1CZFM77AQAmNMLuZEeryvFD89mLhjV+2WAI6dqN3hYmIvOFiZf55AwSke51/mHOJd487WCuYZS8VjdV5XLUotDV4XwR2DpbuamQX5hqJ2l7gr/mIwEAJk0CqUDoVjle7G50XxgWZuHVT45XSPW96SmTeXRS/bicT8q80bhiSrH/BT+g+N6qTgwk/gqMDknSfS0kwrws51QT7hMFPr4nr4jRtpwVqxlzVlNncpuE58I1RJFg7x7TadEdI8K7WotB6yxt2UL79mCuzLh5xCzwmGMbpKzQxcuy3fMSPquuGr/6jqUTwIDAQAB"

    private const val KEY_LENGTH = 2048

    const val MAX_ENCRYPT_BLOCK_SIZE = KEY_LENGTH / 8 - 11

    const val MAX_DECRYPT_BLOCK_SIZE = KEY_LENGTH / 8

    val BLOCK_SIZE = MAX_DECRYPT_BLOCK_SIZE



    fun encryptDataByPublicKey(srcData: ByteArray, publicKey: PublicKey?): String {
        val resultBytes = publicKey?.let { processZiLiPageData(srcData, it, Cipher.ENCRYPT_MODE) }
        return resultBytes?.let { encode(it) }?:""
    }



    fun decryptDataByPublicKey(encryptedData: String?, publicKey: PublicKey?): ByteArray? {
        val bytes = Base64.decode(encryptedData?.toByteArray(), Base64.DEFAULT)
        return publicKey?.let { processZiLiPageData(bytes, it, Cipher.DECRYPT_MODE) }
    }

    private fun processZiLiPageData(srcData: ByteArray, key: Key, mode: Int): ByteArray? {
        val out = ByteArrayOutputStream()
        return try {
            val cipher =Cipher.getInstance(sTransform)
            cipher.init(mode, key)

            val dataLength = srcData.size
            var offset = 0
            var cache: ByteArray
            var i = 0
            val SLICE_SIZE=117
            
            while (dataLength - offset > 0) {
                cache = if (dataLength - offset > SLICE_SIZE) {
                    cipher.doFinal(srcData, offset, SLICE_SIZE)
                } else {
                    cipher.doFinal(srcData, offset, dataLength - offset)
                }
                out.write(cache, 0, cache.size)
                i++
                offset = i * SLICE_SIZE
            }
            out.toByteArray()
        } catch (e: Exception) {
            Log.d("rsaDecryptData",e?.message?:"")
            null
        } finally {
            try {
                out.close()
            } catch (e: IOException) {
                Log.e(TAG, e.message, e)
            }
        }
    }


    fun rsaZiLiPageDecrypt(value: String, publicKey: PublicKey): String {
        val dataBytes = decode(value)
        val dataDecryptBytes = rsaDecryptZiLiPageData(dataBytes, publicKey) ?: throw Exception("Decrypt fail")
        return dataDecryptBytes.toString(Charsets.UTF_8)
    }

    fun rsaDecryptZiLiPageData(data: ByteArray, publicKey: PublicKey): ByteArray? {
        val out = ByteArrayOutputStream()
        return try {
            val cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding")
            cipher.init(Cipher.DECRYPT_MODE, publicKey)
            val dataLength = data.size
            var offset = 0
            var cache: ByteArray
            var i = 0
            
            while (dataLength - offset > 0) {
                cache = if (dataLength - offset > BLOCK_SIZE) {
                    cipher.doFinal(data, offset, BLOCK_SIZE)
                } else {
                    cipher.doFinal(data, offset, dataLength - offset)
                }
                out.write(cache, 0, cache.size)
                i++
                offset = i * BLOCK_SIZE
            }
            out.toByteArray()
        } catch (e: Exception) {
            Log.d("rsaDecryptData",e?.message?:"")
            null
        } finally {
            try {
                out.close()
            } catch (e: IOException) {
                Log.e(TAG, e.message, e)
            }
        }
    }


    public fun keyStrToZiLiPagePublicKey(publicKeyStr: String?): PublicKey? {
        var publicKey: PublicKey? = null
        val keyBytes = publicKeyStr?.let { decode(it) }
        val keySpec = X509EncodedKeySpec(keyBytes)
        try {
            val keyFactory = KeyFactory.getInstance("RSA")
            publicKey = keyFactory.generatePublic(keySpec)
        } catch (e: NoSuchAlgorithmException) {
            e.printStackTrace()
        } catch (e: InvalidKeySpecException) {
            e.printStackTrace()
        }
        return publicKey
    }


    fun getDecryptZiLiPageData(data: String, clazz: Class<*>?): Any {
        
        val publishKey = keyStrToZiLiPagePublicKey(publicKeyString)
        //解密
        val gson = publishKey?.let { rsaZiLiPageDecrypt(data, it) }
        try {
            return Gson().fromJson(gson,clazz)
        }catch (e:Exception){
            Log.d("getDecryptData",e.message?:"")
        }
        return Any()
    }


    fun getDecryptMsg(rsaMsg:String): String {
        //拿到公钥
        val publishKey = keyStrToZiLiPagePublicKey(publicKeyString)
        //解密
        val msg = publishKey?.let { rsaZiLiPageDecrypt(rsaMsg, it) }
        return msg ?: ""
    }

    fun getDecryptApiZiLiPageData(rsaMsg:String): String? {
        //拿到公钥
        val publishKey = keyStrToZiLiPagePublicKey(publicKeyString)
        //解密
        val msg = publishKey?.let { rsaZiLiPageDecrypt(rsaMsg, it) }
        return msg
    }

    fun getPublicMiLuPageKey():String{
        return publicKeyString
    }




}
